#pragma once
#include "util.hpp"
#include <mutex>

/*
    用户数据管理模块
    实现：
    1.用户注册
    2.用户登录
    3.用户数据查询
    4.用户数据修改
*/
class user_table
{
private:
    const char *registrSql = "insert into user values(null, '%s', MD5('%s'), 1000, 0, 0);";
    const char *loginSql = "select id, score, total_count, win_count from user where username='%s' and password=MD5('%s');";
    const char *selectByNameSql = "select id, score, total_count, win_count from user where username='%s';";
    const char *selectByIdSql = "select username, score, total_count, win_count from user where id=%llu;";
    const char *winSql = "update user set score=score+30, total_count=total_count+1, win_count=win_count+1 where id=%llu;";
    const char *loseSql = "update user set score=score-30, total_count=total_count+1 where id=%llu;";

    MYSQL_RES *innerSelect(const char *sql)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if (!_mysql.mysql_exec(sql))
        {
            LOG(DEBUG, "user select fail\n");
            return nullptr;
        }
        MYSQL_RES *res =  mysql_store_result(_mysql.getMysql());
        int row_num = mysql_num_rows(res);
        if (row_num != 1)
        {
            LOG(DEBUG,"the user information queried is not unique or is empty!!\n");
            return nullptr;
        }
        return res;
    }

public:
    user_table(const char *host, const char *user, const char *password, const char *db, unsigned int port)
        : _mysql(host, user, password, db, port)
    {
    }
    ~user_table() {}

    bool registers(Json::Value &user)
    {
        if (user["password"].isNull() || user["username"].isNull())
        {
            LOG(INFO, "INPUT PASSWORD OR USERNAME\n");
            return false;
        }
        char buffer[4096];
        sprintf(buffer, registrSql, user["username"].asCString(), user["password"].asCString());
        if (!_mysql.mysql_exec(buffer))
        {
            LOG(DEBUG, "user register fail\n");
            return false;
        }
        return true;
    }

    bool login(Json::Value &user)
    {
        if (user["password"].isNull() || user["username"].isNull())
        {
            LOG(INFO, "INPUT PASSWORD OR USERNAME\n");
            return false;
        }
        char buffer[4096];
        sprintf(buffer, loginSql, user["username"].asCString(), user["password"].asCString());

        MYSQL_RES *res = innerSelect(buffer);
        if (!res)
            return false;

        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = (Json::UInt64)std::stol(row[0]);
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);
        mysql_free_result(res);
        return true;
    }

    bool select_by_name(const std::string name, Json::Value &user)
    {
        char buffer[4096];
        sprintf(buffer, selectByNameSql, name.c_str());
        MYSQL_RES *res = innerSelect(buffer);
        if (!res)
            return false;

        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = (Json::UInt64)std::stol(row[0]);
        user["username"] = name;
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);
        mysql_free_result(res);
        return true;
    }
    bool select_by_id(uint64_t id, Json::Value &user)
    {
        char buffer[4096];
        sprintf(buffer, selectByIdSql, id);
        MYSQL_RES *res = innerSelect(buffer);
        if (!res)
            return false;

        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = (Json::UInt64)id;
        user["username"] = (std::string)row[0];
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);
        mysql_free_result(res);
        return true;
    }
    bool win(uint64_t id)
    {
        char buffer[4096];
        sprintf(buffer, winSql, id);
        if (!_mysql.mysql_exec(buffer))
        {
            LOG(DEBUG, "update user fail\n");
            return false;
        }
        return true;
    }
    bool lose(uint64_t id)
    {
        char buffer[4096];
        sprintf(buffer, loseSql, id);
        if (!_mysql.mysql_exec(buffer))
        {
            LOG(DEBUG, "update user fail\n");
            return false;
        }
        return true;
    }

private:
    std::mutex _mutex;
    mysql_util _mysql;
};